﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Kyoh.Lib.WinForms
{
    /// <summary>回転して表示する <see cref="Label"/>。</summary>
    public class SpinnableLabel : Label
    {
        private double angle;
        /// <summary>回転する角度を取得・設定します。[degree]</summary>
        [Browsable(true)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DefaultValue(0D)]
        public double Angle
        {
            get { return this.angle; }
            set
            {
                this.angle = value;
                this.Refresh();
            }
        }

        /// <summary>表示するために必要な領域のサイズを取得します。</summary>
        /// <param name="proposedSize">申告されたサイズ。</param>
        /// <returns>必要な領域のサイズ。</returns>
        public override Size GetPreferredSize(Size proposedSize)
        {
            if (this.AutoSize == true)
            {
                //*
                //大きさを調べるためのダミーのBitmapオブジェクトの作成
                using(Bitmap img0 = new Bitmap(1, 1))
                using (Graphics bg0 = Graphics.FromImage(img0))
                {

                    //文字列を描画したときの大きさを計算する
                    var size = bg0.MeasureString(this.Text, this.Font);
                    var w = size.Width;
                    var h = size.Height;

                    var sin = Math.Sin(this.angle * Math.PI / 180);
                    var cos = Math.Cos(this.angle * Math.PI / 180);

                    return new Size((int)(w * cos + h * sin), (int)(w * sin + h * cos));
                }
                /*/
                var size = base.GetPreferredSize(proposedSize);
                var w = size.Width;
                var h = size.Height;

                var sin = Math.Sin(this.angle * Math.PI / 180);
                var cos = Math.Cos(this.angle * Math.PI / 180);

                return new Size((int)(w * cos + h * sin), (int)(w * sin + h * cos));
                //*/
            }
            else
                return base.GetPreferredSize(proposedSize);
        }

        /// <summary>コントロールを描画します。</summary>
        /// <param name="e">描画に使用する <see cref="Graphics"/> 等を保持する <see cref="PaintEventArgs"/>。</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            SizeF size;

            //大きさを調べるためのダミーのBitmapオブジェクトの作成
            using (var img0 = new Bitmap(1, 1))
            using (var bg0 = Graphics.FromImage(img0))
                //文字列を描画したときの大きさを計算する
                size = bg0.MeasureString(this.Text, this.Font);

            var w = size.Width;
            var h = size.Height;

            var sin = Math.Sin(this.angle * Math.PI / 180);
            var cos = Math.Cos(this.angle * Math.PI / 180);

            //Bitmapオブジェクトを作り直す
            using (var img = new Bitmap((int)w, (int)h))
            using (var bg = Graphics.FromImage(img))
            {
                // 描画サイズ
                var psize = this.GetPreferredSize(new Size(1, 1));

                //*
                //imgに文字列を描画する
                bg.DrawString(this.Text, this.Font, Brushes.Black, 0, 0);
                /*/
                var bsize = base.GetPreferredSize(this.Size);
                var barg = new PaintEventArgs(bg, new Rectangle(new Point(0,0), bsize));
                base.OnPaint(barg);
                //*/
                
                // 配置差分
                float dx = 0f, dy = 0f;
                switch (this.TextAlign)
                {
                    case ContentAlignment.BottomCenter:
                        dx = (this.Width - psize.Width) / 2;
                        dy = (this.Height - psize.Height);
                        break;
                    case ContentAlignment.BottomLeft:
                        dx = 0;
                        dy = (this.Height - psize.Height);
                        break;
                    case ContentAlignment.BottomRight:
                        dx = (this.Width - psize.Width);
                        dy = (this.Height - psize.Height);
                        break;
                    case ContentAlignment.MiddleCenter:
                        dx = (this.Width - psize.Width) / 2;
                        dy = (this.Height - psize.Height) / 2;
                        break;
                    case ContentAlignment.MiddleLeft:
                        dx = 0;
                        dy = (this.Height - psize.Height) / 2;
                        break;
                    case ContentAlignment.MiddleRight:
                        dx = (this.Width - psize.Width);
                        dy = (this.Height - psize.Height) / 2;
                        break;
                    case ContentAlignment.TopCenter:
                        dx = (this.Width - psize.Width) / 2;
                        dy = 0;
                        break;
                    case ContentAlignment.TopLeft:
                        dx = 0;
                        dy = 0;
                        break;
                    case ContentAlignment.TopRight:
                        dx = (this.Width - psize.Width);
                        dy = 0;
                        break;
                }

                // 回転するための座標を計算
                // 新しい座標位置を計算する
                //PointF配列を作成
                PointF[] destinationPoints = {new Point((int)(h * sin + dx), (int)(0 + dy)),
                                    new Point((int)(h * sin + w * cos + dx), (int)(w * sin + dy)),
                                    new Point((int)(0 + dx), (int)(h * cos + dy))};

                //画像を描画
                e.Graphics.DrawImage(img, destinationPoints);
            }
        }
    }
}